﻿#include "system_util.h"

#include "datetime/datetime.h"
#include "util/util.h"
#include <string.h>

#include <QDebug>
#include <QProcess>
#ifdef CPPC_OS_LINUX
#include <QFile>
#include <QRegularExpression>
#include <QTextStream>
#include <sys/sysinfo.h>
#include <sys/vfs.h>
#include <unistd.h>
// 网络监控部分
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#endif

#define FILESYSTEM_TYPE_CASE(type, name) \
    case type: result = name; break;

namespace cppc {

/// 获取分区磁盘类型
static std::string getFilesystemType(unsigned long fsType) {
    std::string result;

    switch (fsType) {
        // 添加更多的文件系统类型宏
        FILESYSTEM_TYPE_CASE(0x58465342, "xfs");
        FILESYSTEM_TYPE_CASE(0x9123683E, "btrfs");
        FILESYSTEM_TYPE_CASE(0x0B, "fat32");
        FILESYSTEM_TYPE_CASE(0x5346544E, "ntfs");
        FILESYSTEM_TYPE_CASE(0x414A53, "efs");
        FILESYSTEM_TYPE_CASE(0x201352F6, "exfat");
        FILESYSTEM_TYPE_CASE(0xEF53, "ext");
    default: result = "unknown:" + fsType;
    }

    return result;
}

DiskPartitionInfo SystemUtil::getDiskPartitionInfo(const std::string &path) {
    DiskPartitionInfo diskInfo;

#ifdef CPPC_OS_LINUX
    struct statfs statDiskInfo;
    if (0 == statfs(path.c_str(), &statDiskInfo)) {
        unsigned long long totalBlocks = statDiskInfo.f_bsize;
        diskInfo.totalSize = totalBlocks * statDiskInfo.f_blocks;
        /*
         * f_bfree和f_bavail两个值的区别，前者是硬盘所有剩余空间，后者为非root用户剩余空间。
         * 一般ext3文件系统会给root留5%的独享空间，这个值是仅仅给root用的，普通用户用不了，目的是防止文件系统的碎片。
         */
        diskInfo.freeSize = totalBlocks * statDiskInfo.f_bavail;
        diskInfo.usage = (diskInfo.totalSize - diskInfo.freeSize) * 100.0 / diskInfo.totalSize;
        diskInfo.path = path;
        diskInfo.type = getFilesystemType(statDiskInfo.f_type);
    } else {
        diskInfo.type = "";
        diskInfo.totalSize = 0;
        diskInfo.freeSize = 0;
        diskInfo.usage = 0;
    }
#endif
    return diskInfo;
}

RamInfo SystemUtil::getRamInfo() {
    RamInfo ramInfo;
#ifdef CPPC_OS_LINUX
    //  /proc/meminfo中包含系统允许内存信息
    FILE *fd = fopen("/proc/meminfo", "r");
    if (fd == NULL) {
        ramInfo.freeSize = 0;
        ramInfo.totalSize = 0;
        ramInfo.usage = 0;
        return ramInfo;
    }

    size_t bytesRead;
    int readIndex;
    char *line = NULL;
    int index = 0;
    unsigned long long totalRam = 0;
    unsigned long long freeRam = 0;

    while ((readIndex = getline(&line, &bytesRead, fd)) != -1) {
        if (strstr(line, "MemTotal") != NULL) {
            sscanf(line, "%*s%llu%*s", &totalRam);
        } else if (strstr(line, "MemAvailable") != NULL) {
            sscanf(line, "%*s%llu%*s", &freeRam);
            break;
        }
    }

    fclose(fd);

    ramInfo.totalSize = totalRam * 1024;
    ramInfo.freeSize = freeRam * 1024;
    ramInfo.usage = (ramInfo.totalSize - ramInfo.freeSize) * 100.0 / ramInfo.totalSize;
#endif
    return ramInfo;
}

struct CpuOccupy SystemUtil::getCpuOccupy() {
    CpuOccupy cpu;

    /* /proc/stat 包含了系统启动以来的许多关于kernel和系统的统计信息，
    其中包括CPU运行情况、中断统计、启动时间、上下文切换次数、
    运行中的进程等等信息 */

    char buff[512] = {};
    FILE *fd = fopen("/proc/stat", "r");
    if (fd == NULL) {
        return cpu;
    }
    fgets(buff, sizeof(buff), fd);

    char name[16] = {};
    sscanf(buff, "%s %u %u %u %u", name, &cpu.user, &cpu.nice, &cpu.system, &cpu.idle);
    fclose(fd);
    cpu.name = name;
    return cpu;
}

struct CpuInfo SystemUtil::calCpuInfo(const CpuOccupy &begin, const CpuOccupy &end) {
    CpuInfo cpuUsage;

    double usedCpu = (double)(end.user + end.nice + end.system + end.idle)
                     - (double)(begin.user + begin.nice + begin.system + begin.idle);
    if (usedCpu > 0) {
        double user = (double)(end.user - begin.user);
        double sys = (double)(end.system - begin.system);
        double nice = (double)(end.nice - begin.nice);
        double idle = (double)(end.idle - begin.idle);

        cpuUsage.user = ((int)(10000 * user / usedCpu)) / 100.0;
        cpuUsage.system = ((int)(10000 * sys / usedCpu)) / 100.0;
        // cpuUsage.nice = (100.0 * nice / usedCpu);
        cpuUsage.usage = cpuUsage.user + cpuUsage.system;
    }
    return cpuUsage;
}

std::vector<NetInterfaceInfo> SystemUtil::getNetInterfaceList() {
    std::vector<NetInterfaceInfo> netList;

#ifdef CPPC_OS_LINUX
    int fd;
    struct ifreq buf[16];
    struct ifconf ifc;
    struct ifreq ifrcopy;

    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        close(fd);
        return netList;
    }

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = (caddr_t)buf;

    if (ioctl(fd, SIOCGIFCONF, (char *)&ifc)) {
        close(fd);
        return netList;
    }

    int num = ifc.ifc_len / sizeof(struct ifreq); // 网卡个数
    num = std::min(num, 16);
    char name[16] = {};
    char ip[16] = {};
    char mac[18] = {};
    while ((num--) > 0) {
        memset(name, 0, 16);
        memset(ip, 0, 16);
        memset(mac, 0, 18);

        ifrcopy = buf[num];
        strcpy(name, ifrcopy.ifr_name);

        if (!(ioctl(fd, SIOCGIFADDR, (char *)&ifrcopy))) {
            snprintf(ip, sizeof(ip), "%s",
                     (char *)inet_ntoa(((struct sockaddr_in *)&(ifrcopy.ifr_addr))->sin_addr));
        } else {
            continue;
        }

        if (!ioctl(fd, SIOCGIFHWADDR, (char *)(&ifrcopy))) {
            snprintf(mac, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[0],
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[1],
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[2],
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[3],
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[4],
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[5]);
        } else {
            continue;
        }
        NetInterfaceInfo netInfo;
        netInfo.name = name;
        netInfo.ip = ip;
        netInfo.mac = mac;
        netList.push_back(netInfo);
    }
    close(fd);
#endif
    return netList;
}

NetInterfaceInfo SystemUtil::getNetInterface(const std::string &netName) {
    NetInterfaceInfo netInfo;
#ifdef CPPC_OS_LINUX
    int fd;
    struct ifreq buf[16];
    struct ifconf ifc;
    struct ifreq ifrcopy;

    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        close(fd);
        return netInfo;
    }

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = (caddr_t)buf;

    if (ioctl(fd, SIOCGIFCONF, (char *)&ifc)) {
        close(fd);
        return netInfo;
    }

    int num = ifc.ifc_len / sizeof(struct ifreq); // 网卡个数
    num = std::min(num, 16);
    char name[16] = {};
    char ip[16] = {};
    char mac[18] = {};
    while ((num--) > 0) {
        memset(name, 0, 16);
        memset(ip, 0, 16);
        memset(mac, 0, 18);

        ifrcopy = buf[num];
        strcpy(name, ifrcopy.ifr_name);

        if (!(ioctl(fd, SIOCGIFADDR, (char *)&ifrcopy))) {
            snprintf(ip, sizeof(ip), "%s",
                     (char *)inet_ntoa(((struct sockaddr_in *)&(ifrcopy.ifr_addr))->sin_addr));
        } else {
            continue;
        }

        if (!ioctl(fd, SIOCGIFHWADDR, (char *)(&ifrcopy))) {
            snprintf(mac, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[0],
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[1],
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[2],
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[3],
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[4],
                     (unsigned char)ifrcopy.ifr_hwaddr.sa_data[5]);
        } else {
            continue;
        }

        if (strstr(name, netName.c_str()) != NULL) {
            netInfo.name = netName;
            netInfo.ip = ip;
            netInfo.mac = mac;
            break;
        }
    }
    close(fd);
#endif
    return netInfo;
}

/// 获取网卡实时累计流量
/// \param netName 网卡名称
/// \return <发送字节，接收字节，统计时间>
NetInterfaceFlow SystemUtil::getNetInterfaceFlow(const std::string &netName) {
    NetInterfaceFlow netFlow;
#ifdef CPPC_OS_LINUX
    FILE *net_dev_file = fopen("/proc/net/dev", "r");
    if (net_dev_file == NULL) {
        return netFlow;
    }

    char strRx[32] = {};
    char strTx[32] = {};
    size_t bytesRead;
    int readIndex;

    // 从第三行开始读取网络接口数据
    int i = 0;
    char *line = NULL;
    while ((readIndex = getline(&line, &bytesRead, net_dev_file)) != -1) {
        if ((++i) <= 2) {
            continue;
        }
        if (strstr(line, netName.c_str()) != NULL) {
            sscanf(line, "%*s%s%*s%*s%*s%*s%*s%*s%*s%s", strRx, strTx);

            netFlow.txBytes = atol(strTx);
            netFlow.rxBytes = atol(strRx);
            netFlow.tickCount = cppc::DateTime::currentTickCount();
            netFlow.timeStamp = cppc::DateTime::currentTimeStamp();
            break;
        }
    }
    fclose(net_dev_file);
#endif
    return netFlow;
}

std::vector<NetInterfaceFlow>
SystemUtil ::getNetInterfaceFlow(const std::vector<NetInterfaceInfo> &netList) {
    std::vector<NetInterfaceFlow> flowList;

#ifdef CPPC_OS_LINUX
    FILE *net_dev_file = fopen("/proc/net/dev", "r");
    if (net_dev_file == NULL) {
        return flowList;
    }

    char strRx[32] = {};
    char strTx[32] = {};
    size_t bytesRead;
    int readIndex;

    int i = 0;
    char *line = NULL;
    while ((readIndex = getline(&line, &bytesRead, net_dev_file)) != -1) {
        if ((++i) <= 2) {
            continue;
        }
        // 从第三行开始读取网络接口数据
        for (uint k = 0; k < netList.size(); k++) {
            auto &net = netList.at(k);
            if (strstr(line, net.name.c_str()) != NULL) {
                memset(strRx, 0x0, 32);
                memset(strTx, 0x0, 32);

                sscanf(line, "%*s%s%*s%*s%*s%*s%*s%*s%*s%s", strRx, strTx);

                NetInterfaceFlow flow;
                flow.valid = true;
                flow.name = net.name;
                flow.tickCount = cppc::DateTime::currentTickCount();
                flow.timeStamp = cppc::DateTime::currentTimeStamp();
                flow.txBytes = atol(strTx);
                flow.rxBytes = atol(strRx);
                flowList.push_back(flow);
            }
        }
    }
    fclose(net_dev_file);
#endif
    return flowList;
}

NetInterfaceSpeed SystemUtil ::calNetInterfaceSpeed(const NetInterfaceFlow &begin,
                                                    const NetInterfaceFlow &end) {
    NetInterfaceSpeed speed;

    long time_lapse = (end.tickCount) - (begin.tickCount);
    if (time_lapse > 0 && end.rxBytes >= begin.rxBytes && end.txBytes >= begin.txBytes) {
        speed.valid = true;
        speed.rxSpeed = (end.rxBytes - begin.rxBytes) * 1.0 / (time_lapse * 1.0 / 1000);
        speed.txSpeed = (end.txBytes - begin.txBytes) * 1.0 / (time_lapse * 1.0 / 1000);
    }
    return speed;
}

QString SystemUtil::getDiskSerialNumber() {
    // 创建 QProcess 对象
    QProcess process;

    // 设置命令和参数
    QString command = "lsblk";
    QStringList arguments;
    arguments << "-o" << "SERIAL" << "-nd";

    // 启动进程并等待完成
    process.start(command, arguments);
    process.waitForFinished();

    // 获取命令输出
    QString output = process.readAllStandardOutput().trimmed();

    // 如果有多个硬盘序列号，可以通过换行符分割
    QStringList serialNumbers = output.split('\n', QString::SkipEmptyParts);

    if (!serialNumbers.isEmpty()) {
        // 返回第一个硬盘的序列号
        return serialNumbers.first();
    }

    // 如果未找到序列号，返回空字符串
    return QString();
}
} // namespace cppc
